package cn.crex1.netty.server;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ReferenceCountUtil;

import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author zzh
 */
public class Client1 {

    private Bootstrap client;

    private EventLoopGroup workGruop;

    private ChannelFuture channelFuture;

    private ReconnectTask reconnectTask;

    /**
     * 初始化标识
     */
    private Boolean initFlag = Boolean.FALSE;

    /**
     * 重新连接开始标识
     */
    private Boolean reConStartFlag = Boolean.FALSE;

    /**
     * 重新连接结束标识
     */
    private Boolean reConEndFlag = Boolean.FALSE;

    /**
     * 心跳标识
     */
    private Boolean heartbeatFlag = Boolean.FALSE;

    /**
     * 激活标识 避免多次创建任务
     */
    private Boolean hearbeatActiveFlag = Boolean.TRUE;

    public Client1() {
        init();
    }

    private void init() {
        this.client = new Bootstrap();
        this.workGruop = new NioEventLoopGroup();
        this.reconnectTask=new ReconnectTask();
        client.group(workGruop);
        client.channel(NioSocketChannel.class);
        client.remoteAddress("127.0.0.1", 8881);
        client.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
//                ch.pipeline().addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                ch.pipeline().addLast(new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS));
                ch.pipeline().addLast("strEncoder", new StringEncoder());
                ch.pipeline().addLast("strDecoder", new StringDecoder());
                ch.pipeline().addLast(new ClientHanler());
            }
        });
        //禁用nagle算法,有消息立即发送
        client.option(ChannelOption.TCP_NODELAY, true);
        //保持连接（可以不写，默认为true）
        client.option(ChannelOption.SO_KEEPALIVE, true);

    }

    public void start() {
        Connect();
        activeTask(reconnectTask);
    }

    /**
     * 连接
     */
    public void Connect() {
        try {
            channelFuture = client.connect().sync();
            if (channelFuture.isSuccess()) {
                initFlag = true;
                reConStartFlag = false;
                reconnectTask.setReConCount(0);
                System.out.println("Connect to server successfully!");
            }
        } catch (Exception e) {
            System.out.println("客户端连接失败!" + e.getMessage());
        }

    }

    /**
     * 重连
     */
    public void reconnect() {
        Connect();
    }

    public void activeTask(Runnable runnable) {
        System.out.println("定时任务激活");
        ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5);
        scheduled.scheduleWithFixedDelay(runnable, 0, 5, TimeUnit.SECONDS);
    }

    public void send(String str) {
        channelFuture.channel().writeAndFlush(str + "\n");
        heartbeatFlag = false;
    }

    /**
     * 心跳任务类
     */
    private class HeartbeatTask implements Runnable {
        /**
         * 客户端请求的心跳命令
         */
        private final String HEARTBEAT_SEQUENCE = "hb_request";

        private IdleStateEvent idleStateEvent;

        private ChannelHandlerContext ctx;

        private int startTime = LocalDateTime.now().getMinute();

        public HeartbeatTask(ChannelHandlerContext ctx, IdleStateEvent event) {
            this.idleStateEvent = event;
            this.ctx = ctx;
        }

        @Override
        public void run() {
            if (heartbeatFlag) {
                switch (idleStateEvent.state()) {
                    case WRITER_IDLE:
                        sendHeartbeat(ctx, startTime);
                        break;
                    case READER_IDLE:
                        ;
                        break;
                    case ALL_IDLE:
                        ;
                        break;
                    default:
                        break;
                }
            }
        }

        private void sendHeartbeat(ChannelHandlerContext ctx, int startTime) {
            int endMinute = LocalDateTime.now().getMinute();
            if (endMinute - startTime <= 10) {
                ctx.writeAndFlush(HEARTBEAT_SEQUENCE);
            } else {
                System.out.println("空闲超过10分钟");
            }
        }
    }

    /**
     * 重连任务类
     */
    private class ReconnectTask implements Runnable {
        /**
         * 重连计数
         */
        private Integer reConCount = 0;

        /**
         * 最大重连次数
         */
        private Integer reConCount_MAX = 3;

        @Override
        public void run() {
            if (reConStartFlag) {
                if (reConCount < reConCount_MAX) {
                    System.out.println("第" + (reConCount + 1) + "次重连尝试");
                    reconnect();
                    reConCount++;
                }
            }
        }

        public void setReConCount(Integer reConCount) {
            this.reConCount = reConCount;
        }
    }

    @ChannelHandler.Sharable
    private class ClientHanler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("建立连接时：" + LocalDateTime.now());
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            if (!reConEndFlag){
                reConStartFlag=true;
            }
            System.out.println("关闭连接时：" + LocalDateTime.now());
        }

        /**
         * 心跳请求处理
         * 每4秒发送一次心跳请求;
         */
        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception {
            if (initFlag) {
                if (obj instanceof IdleStateEvent) {
                    IdleStateEvent event = (IdleStateEvent) obj;
                    if (hearbeatActiveFlag) {
                        activeTask(new HeartbeatTask(ctx, event));
                        hearbeatActiveFlag=false;
                    }
                    heartbeatFlag = true;
                }
            }
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("进入处理器");
            if (msg instanceof String) {
                System.out.println(msg);
                if ("close_channel".equals(msg)){
                    //服务器的关闭channel指令 不重新连接
                    reConEndFlag=true;
                }
            }
            ReferenceCountUtil.release(msg);
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            super.channelReadComplete(ctx);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }
}
